package no.ntnu.fp.server;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import no.ntnu.fp.net.co.Connection;
import no.ntnu.fp.net.co.ConnectionImpl;
import no.ntnu.fp.net.db.CalendarDatabase;
import no.ntnu.fp.net.co.MessageType;

public class ServerThread extends Thread {	
	private Connection server;
	private List<ConnectionThread> activeClients;
	private CalendarDatabase database;
	
	public ServerThread( Connection server, CalendarDatabase database ){
		super("CalendarServerThread");
		this.server = server;
		this.database = database;
		setDaemon(true);
		setPriority(Thread.MAX_PRIORITY);
		activeClients = Collections.synchronizedList( new ArrayList<ConnectionThread>() );
		start();
	}	
	
	/**
	 * Main server thread loop, where we listen to new clients that try to connect and handle them
	 */
	public void run() {
		while( !isInterrupted() ) {
			try {
				Connection client;
				
				//Listen for new connections
				client = server.accept();
				
				//Did someone try to connect?
				if( client != null ) {
					addClient( (ConnectionImpl) client );
				}
				
			} catch (SocketTimeoutException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	    try {
			server.close();
		} catch (IOException e) {
			Console.printError("ERROR: could not shutdown server connection! - " + e);
		}
	}
	
	/**
	 * Adds a new client to our connected list, but only if they identify themselves with the correct password and
	 * username. If this fails, they are simply disconnected.
	 */
	public void addClient(ConnectionImpl client) throws IOException {
		Console.printConsole("New client is trying to connect: " + client.getLocalAddress() + " on port " + client.getLocalPort() + "\nRequesting username and password.");
		String message;
		try {
			message = client.receive(5000);
			String[] args = message.split(";");
			String fullName = database.confirmLoginData(args[1], args[2]);
			if( !fullName.isEmpty() ) {
				Console.printConsole("Connection accepted, user logged in as: " + fullName + "(" + args[1] + ")");
				activeClients.add( new ConnectionThread(this, client) );
				client.send( "true;" + fullName + ";" );
				return;
			}
		} catch (Exception e) {
			//Ignore
		}
		Console.printConsole("Invalid username or password! Disconnecting client.");
		client.send( "false;" );
		client.close();
	}

	/**
	 * Is called whenever a client has disconnected from us
	 * @param client which client has disconnected
	 */
	public void disconnectClient(ConnectionThread client) {
		activeClients.remove( client );
		Console.printConsole("Connection closed to "+ client.getName() +"...");
	}

	/**
	 * Takes a raw string from a packet and do whatever it said
	 * @param message
	 * @return the servers answer to the query (usually data or rows affected)
	 */
	public String translateMessage(String message) {
		String[] args = message.split(";");
		try {
			//translate message types (arg[0] is message type)
			switch( MessageType.valueOf(args[0]) ) 
			{
				case MSG_UPDATE_NOTIFICATION: return database.setNotificationStatus(args[1], args[2], args[3]);
				case MSG_REMOVE_USER_FROM_APPOINTMENT: return database.removeUserFromAppointment(args[1], args[2], args[3], args[4]);
				case MSG_GET_APPOINTMENT_REPLY: return database.getAppointmentReplies(args[1]);
				case MSG_GET_NOTIFICATIONS: return database.getNotifications(args[1]);
				case MSG_GET_PARTICIPANTS: return database.getParticipants(args[1]);
				case MSG_GET_ALL_MEETING_ROOMS: return database.getAllMeetingRooms();
				case MSG_GET_FREE_MEETING_ROOMS: return database.getFreeMeetingRooms(args[1], args[2], args[3], args[4], args[5]);
				case MSG_DELETE_APPOINTMENT: return database.cancelAppointment(args[1], args[2], args[3]);
				case MSG_CREATE_APPOINTMENT:
					String participants[] = new String[args.length-9];
					System.arraycopy(args, 9, participants, 0, participants.length);
					return database.addAppointment(args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], participants);
				case MSG_GET_ALL_USERS: return database.getAllUsers();
				case MSG_CREATE_USER: return database.CreateNewUser(args[1], args[2], args[3]);
				case MSG_GET_APPOINTMENTS: return database.getAppointments(args[1]);
				default: Console.printError("Unhandled message type! (message type " + args[0] + ")");
			}
		} catch (SQLException e) {
			Console.printError("Invalid SQL! (message type " + args[0] + ") - " + e);
		} catch (ArrayIndexOutOfBoundsException e) {
			Console.printError("Expected more arguments from message! (message type " + args[0] + ")");
		} catch (IllegalArgumentException e) {
			Console.printError("Unknown message type: " + args[0] + "!");
		}
		
		//Something went wrong...
		return "";
	}
}
